#include "../include/CLevelRenderer.h"
#include "../include/CSDLManager.h"
#include "../include/CRenderBackground.h"
#include "../include/CRenderPlayer.h"
#include "../include/CRenderEnemy.h"

#include "../include/CPlayerBehaviour.h"
#include "../include/CDefaultEnemyBehaviour.h"
#include "../include/CDiagonalEnemyBehaviour.h"
#include "../include/CHorizontalEnemyBehaviour.h"
#include "../include/CBoatEnemyBehaviour.h"
#include "../include/CDefaultPowerUpBehaviour.h"
#include "../include/CShotPowerUpBehaviour.h"

#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <SDL/SDL_ttf.h>
#include <assert.h>
#include <math.h>

//**************************
//Description : Constructor
//Parameters : None
//Note : Private because a single instance is needed
//**************************
CLevelRenderer::CLevelRenderer()
        : m_sNbPlayers(0),
        m_sCurrentPlayer(0),
        m_sNbEnemies(0)
{
}

//**************************
//Description : Constructor
//Parameters : None
//Note : Private because a single instance is needed
//**************************
CLevelRenderer::~CLevelRenderer()
{
}

//**************************
//Description : Init the instance
//Parameters : None
//Return Value : None
//Note : None
//**************************
void CLevelRenderer::Init()
{
    m_SDLManager = CSDLManager::GetInstancePtr();

    m_sCurrentLevel = NULL;

    m_Background = NULL;

    m_vPlayers = NULL;

    m_vEnemies = NULL;

    m_Highscores = m_SDLManager->GetHighscoreGameSurfaces();

    m_Lives = m_SDLManager->GetLivesSurfaces();

    m_Numbers = m_SDLManager->GetNumbersSurfaces();

    LoadHighScore();
}

//**************************
//Description : DeInit the instance
//Parameters : None
//Return Value : None
//Note : None
//**************************
void CLevelRenderer::DeInit()
{
    if ( m_sCurrentLevel != NULL )
        delete[] m_sCurrentLevel;

    if ( m_Background != NULL )
        delete m_Background;

    if ( m_vPlayers != NULL )
        delete[] m_vPlayers;

    if ( m_vEnemies != NULL )
        delete[] m_vEnemies;
}

//**************************
//Description : Load the level _szLevel
//Parameters : Pointer toward the current level configuration file
//Return Value : true if success, otherwise false
//Note : Have to be called after the SDL initialisation because we need a screen
//**************************
bool CLevelRenderer::LoadLevel(const char * _szLevelDir)
{
    char szBuffer[255];        //buffer of a line
    char szBackgroundName[255];    //name of the background bind to ther level
    int iTypeEnemy,         // type of the enemy
    iX,                 // value of the abscissa
    iY,                 // value of the ordinary
    iSpeedX,            // value of the m_sCurrentLevelspeed on the abscissa
    iSpeedY,            // value of the speed on the ordinary
    iPowerUp,           // 0 if no powerup, 1 if a powerup
    iTypePowerUp,       // type of the power up
    iTypeShotPowerUp,       // if it's a shot power up, we have to know the type of shot that is changed
    iBackgroundSpeed;   //speed of the scrolling
    long lEnemyAppearance;  //time before the appearance of an enemy

    SDL_Surface * Screen = m_SDLManager->GetScreen();

    FILE *fFile;				//pointer on the file

    fFile = fopen(_szLevelDir, "r");
    //test on the opening
    assert ( fFile != NULL );

    //get the first line
    fgets(szBuffer, 255, fFile);
    //scan the background
    sscanf(szBuffer, "BACKGROUND: %s %d", szBackgroundName, &iBackgroundSpeed);

    //We create the animation
    CSpriteAnimation BackAnim(szBackgroundName);
    //we create the CRenderBackground
    m_Background = new CRenderBackground(Screen, &BackAnim, iBackgroundSpeed);

    //get the second line
    fgets(szBuffer, 255, fFile);
    //scan the number of enemies
    sscanf(szBuffer, "ENNEMIES: %hd", &m_sNbEnemies);

    m_vEnemies = new CRenderEnemy[m_sNbEnemies]();

    short sCount = 0;

    while ( !feof(fFile) && sCount < m_sNbEnemies )
    {
        //get a line
        fgets(szBuffer, 255, fFile);

        //check wether it's useful or not (comment line, blank, or nothing)
        if (szBuffer[0] != '#' && szBuffer[0] != '\r' && szBuffer[0] != '\0'
                && szBuffer[0] != '\n' && strlen(szBuffer) != 0)
        {
            //get info of the ennemy
            sscanf(szBuffer, "%d %d %d %d %d %d %d %d %ld", &iTypeEnemy,
                   &iX, &iY,
                   &iSpeedX, &iSpeedY,
                   &iPowerUp, &iTypePowerUp, &iTypeShotPowerUp,
                   &lEnemyAppearance);

            CDefaultEnemyBehaviour * Enemy;

            //create the enemy
            switch (iTypeEnemy)
            {
                case DEFAULTENEMY:
                {
                    CDefaultEnemyBehaviour * DefaultEnemy = new CDefaultEnemyBehaviour(iX, iY, iSpeedX, iBackgroundSpeed+iSpeedY);
                    Enemy = DefaultEnemy;
                    break;
                }
                case DIAGONALENEMY:
                {
                    CDiagonalEnemyBehaviour * DiagonalEnemy = new CDiagonalEnemyBehaviour(iX, iY, iSpeedX, iBackgroundSpeed+iSpeedY);
                    Enemy = DiagonalEnemy;
                    break;
                }
                case HORIZONTALENEMY:
                {
                    CHorizontalEnemyBehaviour * HorizontalEnemy = new CHorizontalEnemyBehaviour(iX, iY, iSpeedX, iBackgroundSpeed+iSpeedY);
                    Enemy = HorizontalEnemy;
                    break;
                }
                case BOATENEMY:
                {
                    CBoatEnemyBehaviour * BoatEnemy = new CBoatEnemyBehaviour(iX, iY, iSpeedX, iBackgroundSpeed+iSpeedY);
                    Enemy = BoatEnemy;
                    break;
                }
            }

            m_vEnemies[sCount].SetRender(Screen,
                                    Enemy,
                                    lEnemyAppearance,
                                    m_SDLManager->GetEnemyAnimations(iTypeEnemy),
                                    m_SDLManager->GetEnemyShotsAnimation(iTypeEnemy));

            //if there is a powerup
            if ( iPowerUp == 1 )
            {
                //create the powerup
                switch (iTypePowerUp)
                {
                case DEFAULTPOWERUP:
                {
                    CDefaultPowerUpBehaviour * DefaultPowerUp = new CDefaultPowerUpBehaviour();
                    m_vEnemies[sCount].SetPowerUpBehaviour(DefaultPowerUp, m_SDLManager->GetPowerUpAnimations(iTypePowerUp) );
                    break;
                }
                case SHOTPOWERUP:
                {
                    CShotPowerUpBehaviour * ShotPowerUp = new CShotPowerUpBehaviour();
                    ShotPowerUp->SetTypeShotBonus(iTypeShotPowerUp);
                    m_vEnemies[sCount].SetPowerUpBehaviour(ShotPowerUp, m_SDLManager->GetPowerUpAnimations(iTypePowerUp) );
                    break;
                }
                }
            }

            sCount++;
        }
    }

    fclose(fFile);

    return true;
}

//**************************
//Description : Load the last game saved
//Parameters : The path to the game
//Return Value : true if success, otherwise false
//Note : Have to be called after the SDL initialisation because we need a screen
//**************************
bool CLevelRenderer::LoadGame(const char * _szLoad)
{
    FILE *fLoadGame;
    short sCurrentLevel; //current level of a player
    int iLives,     //number of lives of a player
        iEnemiesKilled; //number of ennemies that a player killed
    long lCurrentTime,  //time spent from the begining of the current level
        lPoints;    //points of the player
    int iNbShots[2]; //number of shot for each type of shot of the player
    SDL_Surface * Screen = m_SDLManager->GetScreen();

    fLoadGame = fopen(_szLoad, "rb");
    assert( fLoadGame != NULL );

    //we load the number of players
    fread(&m_sNbPlayers, sizeof(short), 1, fLoadGame);

    //we load the time from the begining of the level
    fread(&lCurrentTime, sizeof(long), 1, fLoadGame);

    //we load the number of the player who is currently playing
    fread(&m_sCurrentPlayer, sizeof(short), 1, fLoadGame);

    //we create necessary variables
    m_vPlayers = new CRenderPlayer[m_sNbPlayers]();
    m_sCurrentLevel = new short[m_sNbPlayers]();

    //we load the two players
    for (int i = 0; i < m_sNbPlayers; i++)
    {
        CPlayerBehaviour * Player = new CPlayerBehaviour();

        //we store its lives, its points and the number of enemies that he killed
        fread(&sCurrentLevel, sizeof(short), 1, fLoadGame);
        fread(&iLives, sizeof(int), 1, fLoadGame);
        fread(&lPoints, sizeof(long), 1, fLoadGame);
        fread(&iEnemiesKilled, sizeof(int), 1, fLoadGame);

        //we go until we have the TWO shots (primary and secondary)
        for (int j = 0; j < 2; j++)
        {
            fread(&iNbShots[j], sizeof(int), 1, fLoadGame);
        }

        //we set the current level
        m_sCurrentLevel[i] = sCurrentLevel;

        //we set the lives
        Player->AddLife(iLives - Player->HasLives());
        //we set its points
        Player->AddPoints(lPoints);
        //we set its number of enemies killed
        Player->AddEnemiesKilled(iEnemiesKilled);
        //we set the number of shots
        Player->SetNbShots(iNbShots);

        m_vPlayers[i].SetRender(Screen,
                                    Player,
                                    m_SDLManager->GetPlayerAnimations(i),
                                    m_SDLManager->GetPlayerShotsAnimation(i));

        Player = NULL;
    }

    //we load the current level
    char szCurrentLevel[2];

    sprintf(szCurrentLevel, "%hd", m_sCurrentLevel[m_sCurrentPlayer]);

    std::string szStringCurrentLevel;
    szStringCurrentLevel += szLevelsDir;
    szStringCurrentLevel += "Level";
    szStringCurrentLevel += szCurrentLevel;

    LoadLevel( szStringCurrentLevel.c_str() );

    //we record the beginning of the level
    m_lBeginLevel = SDL_GetTicks() - lCurrentTime;

    m_lPauseTime = 0;

    fclose(fLoadGame);

    //we make the enemies that have an appearance lowest than the time of the game disappear (we make their y be after the height of the window)
    int i = 0;

    while ( i < m_sNbEnemies && m_vEnemies[i].GetAppearance() < lCurrentTime )
    {
        m_vEnemies[i].GetEnemyBehaviour()->SetY(GAME_WINDOW_HEIGHT*2);
        i++;
    }

    return true;
}

//**************************
//Description : Save the current game
//Parameters : The path to the save
//Return Value : true if success, otherwise false
//Note : Have to be called after the SDL initialisation because we need a screen
//**************************
bool CLevelRenderer::SaveGame(const char * _szSave)
{
    FILE *fSaveGame;
    short sCurrentLevel; //current level of a player
    int iLives,     //number of lives of a player
        iEnemiesKilled; //number of ennemies that a player killed
    long lCurrentTime,  //time spent from the begining of the current level
        lPoints;    //points of the player
    int * iNbShots; //number of shot for each type of shot of the player

    fSaveGame = fopen(_szSave, "wb");
    assert( fSaveGame != NULL );

    //we get the time from the beginning of the level
    lCurrentTime = SDL_GetTicks() - m_lBeginLevel - m_lPauseTime;

    //we save the number of players
    fwrite(&m_sNbPlayers, sizeof(short), 1, fSaveGame);

    //we save this value
    fwrite(&lCurrentTime, sizeof(long), 1, fSaveGame);

    //we save the player who is currently playing
    fwrite(&m_sCurrentPlayer, sizeof(short), 1, fSaveGame);

    //we save the two players
    for (int i = 0; i < m_sNbPlayers; i++)
    {
        CPlayerBehaviour * Player = m_vPlayers[i].GetPlayerBehaviour();

        //we save info from the player
        sCurrentLevel = m_sCurrentLevel[i];
        iLives = Player->HasLives();
        lPoints = Player->GetPoints();
        iEnemiesKilled = Player->GetEnemiesKilled();
        iNbShots = Player->GetNbShots();

        //we store its current level, itslives, its points and the number of enemies that he killed
        fwrite(&sCurrentLevel, sizeof(short), 1, fSaveGame);
        fwrite(&iLives, sizeof(int), 1, fSaveGame);
        fwrite(&lPoints, sizeof(long), 1, fSaveGame);
        fwrite(&iEnemiesKilled, sizeof(int), 1, fSaveGame);

        //we go until we have the TWO shots (primary and secondary)
        for (int j = 0; j < 2; j++)
        {
            fwrite(&iNbShots[j], sizeof(int), 1, fSaveGame);
        }
    }

    fclose(fSaveGame);

    return true;
}

//**************************
//Description : Restart the current level
//Parameters : None
//Return Value : None
//Note : RestartLevel is called when a player die, because we can't restart a level when we want
//**************************
void CLevelRenderer::RestartLevel()
{
    //we delete the background
    if ( m_Background != NULL )
        delete m_Background;

    //We delete the ennemies
    if ( m_vEnemies != NULL )
        delete[] m_vEnemies;

    //we reinitialise the current player and set the second (if any)
    m_vPlayers[m_sCurrentPlayer].InitRender();

    m_sCurrentPlayer++;

    if (m_sCurrentPlayer >= m_sNbPlayers)
        m_sCurrentPlayer = 0;

    //we init the current player in order to make it blink
    m_vPlayers[m_sCurrentPlayer].InitRender();

    //we load the level for the player
    char szCurrentLevel[2];

    sprintf(szCurrentLevel, "%hd", m_sCurrentLevel[m_sCurrentPlayer]);

    std::string szStringCurrentLevel;
    szStringCurrentLevel += szLevelsDir;
    szStringCurrentLevel += "Level";
    szStringCurrentLevel += szCurrentLevel;

    LoadLevel( szStringCurrentLevel.c_str() );

    //we save the begining of the level
    m_lBeginLevel = SDL_GetTicks();

    m_lPauseTime = 0;
}

//**************************
//Description : Load the highscore of the game
//Parameters : None
//Return Value : true if success, otherwise false
//Note : None
//**************************
bool CLevelRenderer::LoadHighScore()
{
    FILE * fFile;

    fFile = fopen( szHighScoreDir.c_str() , "rb" );

    //test on the opening
    assert ( fFile != NULL );

    do
    {
        fread(&m_lHighScore, sizeof(long), 1, fFile);
    }
    while ( !feof(fFile) );

    fclose(fFile);

	return true;
}

//**************************
//Description : Save the highscore of the game
//Parameters : None
//Return Value : true if success, otherwise false
//Note : None
//**************************
bool CLevelRenderer::SaveHighScore()
{
    FILE * fFile;

    fFile = fopen( szHighScoreDir.c_str(), "wb" );

    //test on the opening
    assert ( fFile != NULL );

    fwrite(&m_lHighScore, sizeof(long), 1, fFile);

    fclose(fFile);

	return true;
}

//**************************
//Description : Return the highscore of the game
//Parameters : None
//Return Value : The high score of the game
//Note : have to be called after LoadHighScore
//**************************
long CLevelRenderer::GetHighScore()
{
    return m_lHighScore;
}

//**************************
//Description : Start the game from the beginning with _sNbPLayers
//Parameters : The number of players
//Return Value : None
//Note : None
//**************************
void CLevelRenderer::StartGame(short _sNbPlayers)
{
    //we test if can start the game
    if ( _sNbPlayers > 0 && _sNbPlayers <= MAX_PLAYERS )
    {
        //we initialise
        m_sCurrentLevel = new short[_sNbPlayers]();
        m_sNbPlayers = _sNbPlayers;
        m_sCurrentPlayer = 0;

        //we initialise each player
        m_vPlayers = new CRenderPlayer[m_sNbPlayers]();

        CPlayerBehaviour * Players = NULL;
        SDL_Surface * Screen = m_SDLManager->GetScreen();

        for (int i = 0; i < m_sNbPlayers; i++)
        {
            Players = new CPlayerBehaviour();

            m_vPlayers[i].SetRender(Screen,
                                    Players,
                                    m_SDLManager->GetPlayerAnimations(i),
                                    m_SDLManager->GetPlayerShotsAnimation(i));
            m_sCurrentLevel[i] = 0;

            Players = NULL;
        }

        //we load the current level
        char szCurrentLevel[2];

        sprintf(szCurrentLevel, "%hd", m_sCurrentLevel[m_sCurrentPlayer]);

        std::string szStringCurrentLevel;
        szStringCurrentLevel += szLevelsDir;
        szStringCurrentLevel += "Level";
        szStringCurrentLevel += szCurrentLevel;

        LoadLevel( szStringCurrentLevel.c_str() );

        //we record the beginning of the level
        m_lBeginLevel = SDL_GetTicks();

        m_lPauseTime = 0;

        //we launch the game
        Game();
    }
}

//**************************
//Description : Start the game from a save file
//Parameters : The save configuration file
//Return Value : None
//Note : None
//**************************
void CLevelRenderer::StartGame(const char * _szLoadLevel)
{
    if ( LoadGame(_szLoadLevel) )
    {
        //we test if can start the game by loading the save
        if ( m_sNbPlayers > 0 && m_sNbPlayers <= MAX_PLAYERS )
        {
            //we launch the game
            Game();
        }
    }
}

//**************************
//Description : Test if the game is over or not
//Parameters : None
//Return Value : True if the game is over, false if not
//Note : The game is over whan all players don't have life anymore or if one player has finished the game
//**************************
bool CLevelRenderer::GameOver()
{
    bool bGameOver = true;
    int i = 0;

    if ( m_vPlayers != NULL )
    {
        //we search partially, because if only one players is alive, the game isn't over
        while (i < m_sNbPlayers && bGameOver)
        {
            bGameOver = (m_vPlayers[i].GetPlayerBehaviour()->IsDead() || (m_sCurrentLevel[i] >= NUMBER_LEVELS) );
            i++;
        }

        //we can declare the end of the game after the animation of death of the current player has ended
        if ( bGameOver )
            bGameOver = m_vPlayers[m_sCurrentPlayer].IsPlayerDead();
    }

    return bGameOver;
}

//**************************
//Description : Test if the current level is over or not
//Parameters : None
//Return Value : True if the lvel is over, false if not
//Note : The level is over when all ennemies went out of the screen (y > GAME_WINDOW_HEIGHT) or are dead
//**************************
bool CLevelRenderer::LevelOver()
{
    //if the current player is dead, we have to restart the level
    if ( m_vPlayers != NULL && (m_vPlayers[m_sCurrentPlayer].IsExploding() || m_vPlayers[m_sCurrentPlayer].IsPlayerExplosed()) )
        return false;

    bool bLevelOver = true;

    //we test if there are enemies
    if ( m_vEnemies != NULL )
    {
        //we test the appearance of the last enemy and we add some time in order to let him move
        long lCurrentTime = SDL_GetTicks();

        int iNumEnemy = 0;

        while (iNumEnemy < m_sNbEnemies && bLevelOver)
        {
            //if the enemy has appeared
            if ( (m_vEnemies[iNumEnemy].GetAppearance() + 2000) < (lCurrentTime - m_lBeginLevel - m_lPauseTime) )
            {
                //we test if the enemy is dead OR if he is out of the screen (he has passed the height of the screen
                bLevelOver = ( m_vEnemies[iNumEnemy].IsEnemyDead()
                            || (m_vEnemies[iNumEnemy].GetEnemyBehaviour()->GetY() - m_vEnemies[iNumEnemy].GetEnemyBehaviour()->GetHeight()/2) > GAME_WINDOW_HEIGHT  );
            }
            else
                bLevelOver = false;

            iNumEnemy++;
        }
    }

    return bLevelOver;
}

//**************************
//Description : Manage collision between all rendering entities
//Parameters : None
//Return Value : True if the players collide with an enemy or a shot, otherwise false
//Note : None
//**************************
bool CLevelRenderer::Collision()
{
    bool bCollision = false;

    //we get the current time
    long lCurrentTime = SDL_GetTicks();

    for (int i = 0; i < m_sNbEnemies; i++)
    {
        //we test the time of appearance
        if ( m_vEnemies[i].GetAppearance() < (lCurrentTime - m_lBeginLevel - m_lPauseTime) )
        {
            if ( m_vPlayers[m_sCurrentPlayer].CollideWithEnemy(&m_vEnemies[i]) )
                bCollision = true;

            m_vEnemies[i].CollideWithPlayer(&m_vPlayers[m_sCurrentPlayer]);
        }
        //else ennemies after this one don't appear because their appearance time is higher
        else
            i = m_sNbEnemies;
    }

    return bCollision;
}

//**************************
//Description : Draw the statistics of the given player at the end of a level, of the game or when it's game over
//Parameters : The number of the player we want to draw the statistics
//Return Value : None
//Note : None
//**************************
void CLevelRenderer::DrawPlayerStatistics(int _iNumPlayer)
{
    std::string szText;
    char szPlayer[100];
    long lHigh = 0;     //highscore of the player
    int iNum;       //number to draw
    int iPow = 1;       //result of pow

    SDL_Color White = { 255, 255, 255 };
    SDL_Color Green = { 0, 255, 0 };
    SDL_Color Red = { 255, 0, 0 };

    SDL_Rect Dest;
    SDL_Surface * Screen = m_SDLManager->GetScreen(), *Text;

    //we get the font
    TTF_Font * BigGameFont = m_SDLManager->GetBigGameFont();
    TTF_Font * LittleGameFont = m_SDLManager->GetLittleGameFont();

    szText.clear();
    sprintf(szPlayer, "%d", _iNumPlayer);
    szText = "Player ";
    szText += szPlayer;

    Text = TTF_RenderText_Blended(LittleGameFont, szText.c_str(), Green);

    Dest.x = 50;
    Dest.y = 150 + (_iNumPlayer-1) * 200;

    SDL_BlitSurface(Text, NULL, Screen, &Dest);

    //if the player is dead, we show it
    if ( m_vPlayers[_iNumPlayer-1].GetPlayerBehaviour()->IsDead() )
    {
        Dest.x = 60 + Text->w;
        Dest.y = 150 + (_iNumPlayer-1) * 200;

        szText.clear();
        szText = "DEAD";

        Text = TTF_RenderText_Blended(LittleGameFont, szText.c_str(), Red);

        SDL_BlitSurface(Text, NULL, Screen, &Dest);
    }

    szText.clear();
    szText = "Highscore :";

    Text = TTF_RenderText_Blended(LittleGameFont, szText.c_str(), White);

    Dest.x = 70;
    Dest.y = 190 + (_iNumPlayer-1) * 200;

    SDL_BlitSurface(Text, NULL, Screen, &Dest);

    lHigh = m_vPlayers[_iNumPlayer-1].GetPlayerBehaviour()->GetPoints();

    //we draw that we have a new highscore
    if ( lHigh > m_lHighScore )
    {
        szText.clear();
        szText = "NEW HIGHSCORE !";

        Text = TTF_RenderText_Blended(LittleGameFont, szText.c_str(), Red);

        Dest.y = 190 + (_iNumPlayer-1) * 200;
        Dest.x = 9 * 40;

        SDL_BlitSurface(Text, NULL, Screen, &Dest);
    }

    //we draw the value of the highscore
    for (int j = 0; j < 9; j++)
    {
        for (int a = 0; a < (8-j); a++)
          iPow *= 10;
        iNum = lHigh / iPow;
        lHigh -= iNum * iPow;

        sprintf(szPlayer, "%d", iNum);

        Text = TTF_RenderText_Blended(LittleGameFont, szPlayer, White);

        Dest.y = 230 + (_iNumPlayer-1) * 200;
        Dest.x = 100 + j * 40;

        SDL_BlitSurface(Text, NULL, Screen, &Dest);

        iPow = 1;
    }

    szText.clear();
    szText = "Enemies killed :";

    Text = TTF_RenderText_Blended(LittleGameFont, szText.c_str(), White);

    Dest.x = 70;
    Dest.y = 270 + (_iNumPlayer-1) * 200;

    SDL_BlitSurface(Text, NULL, Screen, &Dest);

    sprintf(szPlayer, "%d", m_vPlayers[_iNumPlayer-1].GetPlayerBehaviour()->GetEnemiesKilled());

    Text = TTF_RenderText_Blended(LittleGameFont, szPlayer, White);

    Dest.x = 100;
    Dest.y = 310 + (_iNumPlayer-1) * 200;

    SDL_BlitSurface(Text, NULL, Screen, &Dest);
}

//**************************
//Description : Draw the statistics on the screen at the end of the level or at the end of the game
//Parameters : None
//Return Value : None
//Note : None
//**************************
void CLevelRenderer::DrawEndStatistics()
{
    SDL_Rect Dest;
    SDL_Surface * Screen = m_SDLManager->GetScreen();

    // We first fill the entire screen with black
    SDL_FillRect(Screen, NULL, SDL_MapRGB(Screen->format, 0, 0, 0));

    //we get the background of the menu and apply it here
    SDL_Surface * Background = m_SDLManager->GetBackgroundMenu();
    Dest.x = 0;
    Dest.y = 0;

    SDL_BlitSurface(Background, NULL, Screen, &Dest);

    //we get the font
    TTF_Font * BigGameFont = m_SDLManager->GetBigGameFont();
    TTF_Font * LittleGameFont = m_SDLManager->GetLittleGameFont();

    SDL_Color White = { 255, 255, 255 };
    SDL_Color Green = { 0, 255, 0 };
    SDL_Color Red = { 255, 0, 0 };
    SDL_Surface * Text;

    //we are either at the end of a level, or at the end of the game when this function is called
    if ( LevelOver() )
    {
        if ( m_sCurrentLevel[m_sCurrentPlayer] >= (NUMBER_LEVELS-1) )
            Text = TTF_RenderText_Blended(BigGameFont, "Game Finished !", Green);
        else
            Text = TTF_RenderText_Blended(BigGameFont, "Level Clear !", Green);

        Dest.y = 20;
        Dest.x = (WINDOW_WIDTH / 2) - (Text->w / 2);

        SDL_BlitSurface(Text, NULL, Screen, &Dest);

        //we draw the statistics only for the current player
        DrawPlayerStatistics(m_sCurrentPlayer+1);
    }
    else
    {
        Text = TTF_RenderText_Blended(BigGameFont, "Game Statistics :", Green);

        Dest.y = 20;
        Dest.x = (WINDOW_WIDTH / 2) - (Text->w / 2);

        SDL_BlitSurface(Text, NULL, Screen, &Dest);

        //we draw the statistics of the two players
        for (int i = 1; i <= m_sNbPlayers; i++)
            DrawPlayerStatistics(i);
    }

    //We flip the screen
    m_SDLManager->Flip();

    SDL_Delay(4000);
}

//**************************
//Description : Draw the statistics on the screen
//Parameters : None
//Return Value : None
//Note : None
//**************************
void CLevelRenderer::DrawStatistics()
{
    SDL_Surface * Screen = m_SDLManager->GetScreen();

    SDL_Rect Dest;

    long lHigh = 0;     //highscore of the player
    int iNum;       //number to draw
    int iPow = 1;       //result of pow

    //we draw the highscores to the left off the screen
    for (int i = 0; i < m_sNbPlayers; i++)
    {
        Dest.y = i * (2 * m_Highscores[i+1]->h);
        Dest.x = 0;

        SDL_BlitSurface(m_Highscores[i+1], NULL, Screen, &Dest);

        lHigh = m_vPlayers[i].GetPlayerBehaviour()->GetPoints();

        //we draw the value of the highscore
        for (int j = 0; j < 9; j++)
        {
            Dest.y = i * (2 * m_Highscores[i+1]->h) + m_Highscores[i+1]->h;
            Dest.x = j * m_Numbers[j]->w;

            for (int a = 0; a < (8-j); a++)
              iPow *= 10;
            iNum = lHigh / iPow;
            lHigh -= iNum * iPow;

            SDL_BlitSurface(m_Numbers[iNum], NULL, Screen, &Dest);

            iPow = 1;
        }
    }

    //the highscore of the game is drawn to the bottom left
    Dest.y = WINDOW_HEIGHT - (2 * m_Highscores[0]->h);
    Dest.x = 0;

    SDL_BlitSurface(m_Highscores[0], NULL, Screen, &Dest);

    lHigh = m_lHighScore;

    //we draw the value of the highscore
    for (int j = 0; j < 9; j++)
    {
        Dest.y = WINDOW_HEIGHT - m_Highscores[0]->h;
        Dest.x = j * m_Numbers[j]->w;

        for (int a = 0; a < (8-j); a++)
          iPow *= 10;
        iNum = lHigh / iPow;
        lHigh -= iNum * iPow;

        SDL_BlitSurface(m_Numbers[iNum], NULL, Screen, &Dest);

        iPow = 1;
    }

    int iLives;  //number of lives of a player

    //the lives are drawn to the right of the screen
    for (int i = 0; i < m_sNbPlayers; i++)
    {
        Dest.y = i * (2 * m_Lives[i]->h);
        Dest.x = GAME_WINDOW_X + GAME_WINDOW_WIDTH;

        SDL_BlitSurface(m_Lives[i], NULL, Screen, &Dest);

        //we draw the number of lives
        Dest.y = i * (2 * m_Lives[i]->h) + m_Lives[i]->h;
        Dest.x = GAME_WINDOW_X + GAME_WINDOW_WIDTH;

        iLives = m_vPlayers[i].GetPlayerBehaviour()->HasLives();

        SDL_BlitSurface(m_Numbers[iLives], NULL, Screen, &Dest);
    }
}

//**************************
//Description : Draw the screen
//Parameters : None
//Return Value : None
//Note : None
//**************************
void CLevelRenderer::DrawScene()
{
    SDL_Surface * Screen = m_SDLManager->GetScreen();

    // We first fill the entire screen with black
    SDL_FillRect(Screen, NULL, SDL_MapRGB(Screen->format, 0, 0, 0));

    //we draw the statistics
    DrawStatistics();

    //we render the background
    m_Background->Render();

    //we get the current time
    long lCurrentTime = SDL_GetTicks();

    //we render the enemies if they have to appear
    for (int i = 0; i < m_sNbEnemies; i++)
    {
        //we test the time of appearance
        if ( m_vEnemies[i].GetAppearance() < (lCurrentTime - m_lBeginLevel - m_lPauseTime) )
            m_vEnemies[i].Render();
        //else ennemies after this one don't appear because their appearance time is higher
        else
            i = m_sNbEnemies;
    }

    //we render the player
    m_vPlayers[m_sCurrentPlayer].Render();

    //We flip the screen
    m_SDLManager->Flip();
}

//**************************
//Description : Manage events in the game
//Parameters : a boolean, to know if the player quit the game, a boolean to know if we are in pause or not
//Return Value : None
//Note : None
//**************************
void CLevelRenderer::ManageEvents(bool & _bEndGame, bool & _bPauseGame)
{
    SDL_Event event;

    while ( SDL_PollEvent(&event) )
    {
        if ( event.type == SDL_QUIT )
            _bEndGame = true;

        if ( event.type == SDL_KEYDOWN )
        {
            if ( event.key.keysym.sym == SDLK_ESCAPE )
                _bEndGame = true;

            if ( event.key.keysym.sym == SDLK_p )
            {
                _bPauseGame = !_bPauseGame;

                if (_bPauseGame)
                    m_lBeginPause = SDL_GetTicks();
                else
                    m_lPauseTime += SDL_GetTicks() - m_lBeginPause;
            }
            //save the game
            if ( event.key.keysym.sym == SDLK_F5 )
            {
                SaveGame( szSaveGameDir.c_str() );
            }
        }
    }
}

//**************************
//Description : Manage the game
//Parameters : None
//Return Value : None
//Note : None
//**************************
void CLevelRenderer::Game()
{
    bool bPlayerExploded = false;    //allows us to show the animation of the death of the player before switching player
    bool bEndGame = false;           //if escape or sdl_quit is called we return to the menu
    bool bPauseGame = false;           //if P is pressed, Pause is set/unset
    long lCurrentTime, lFormerTime = SDL_GetTicks();

    while ( !bEndGame && !GameOver() )
    {
        lCurrentTime = SDL_GetTicks();

        if ( (lCurrentTime - lFormerTime) > DELAY )
        {
            ManageEvents(bEndGame, bPauseGame);

            if ( !bPauseGame )
            {
                if ( !LevelOver() )
                {
                    if ( !bPlayerExploded )
                        //we test the collision
                        bPlayerExploded = Collision();
                    else
                    {
                        //if the animation of the death of the current player has finished, we switch player and restart the level
                        if ( m_vPlayers[m_sCurrentPlayer].IsPlayerExplosed() )
                        {
                            //we search the first player who don't have finished the game
                            do
                            {
                                m_sCurrentPlayer++;

                                if ( m_sCurrentPlayer >= m_sNbPlayers )
                                    m_sCurrentPlayer = 0;
                            }
                            while ( m_sCurrentLevel[m_sCurrentPlayer] >= NUMBER_LEVELS );

                            m_sCurrentPlayer--;

                            if ( m_sCurrentPlayer < 0 )
                                m_sCurrentPlayer = m_sNbPlayers - 1;

                            RestartLevel();
                            bPlayerExploded = false;
                        }
                    }

                    //we render the screen
                    DrawScene();
                }
                else
                {
                    //show the current statistics
                    DrawEndStatistics();

                    //Delete current enemies and background
                    delete m_Background;
                    m_Background = NULL;
                    delete[] m_vEnemies;
                    m_vEnemies = NULL;

                    //we reinitialise the player for the next level
                    m_vPlayers[m_sCurrentPlayer].InitRender();

                    //next level
                    m_sCurrentLevel[m_sCurrentPlayer]++;

                    //if we are ate the end of the game
                    if ( m_sCurrentLevel[m_sCurrentPlayer] < NUMBER_LEVELS )
                    {
                        //load the next level
                        char szCurrentLevel[2];

                        sprintf(szCurrentLevel, "%hd", m_sCurrentLevel[m_sCurrentPlayer]);

                        std::string szStringCurrentLevel;
                        szStringCurrentLevel += szLevelsDir;
                        szStringCurrentLevel += "Level";
                        szStringCurrentLevel += szCurrentLevel;

                        LoadLevel( szStringCurrentLevel.c_str() );

                        m_lBeginLevel = SDL_GetTicks();
                        m_lPauseTime = 0;
                    }
                    else
                    {
                        bEndGame = true;

                        //the game is finished for this player but maybe not for the others
                        int i = 0;
                        while ( i < m_sNbPlayers && bEndGame )
                        {
                            if ( m_sCurrentLevel[i] < NUMBER_LEVELS )
                                bEndGame = false;

                            i++;
                        }

                        //if the game is not ended, we get to the player before the first who do not have finished the game and restart the level
                        if ( !bEndGame )
                        {
                            while ( m_sCurrentLevel[m_sCurrentPlayer] >= NUMBER_LEVELS )
                            {
                                m_sCurrentPlayer++;

                                if ( m_sCurrentPlayer >= m_sNbPlayers )
                                    m_sCurrentPlayer = 0;
                            }

                            m_sCurrentPlayer--;

                            if ( m_sCurrentPlayer < 0 )
                                m_sCurrentPlayer = m_sNbPlayers - 1;

                            RestartLevel();
                        }
                    }
                }
            }

            lFormerTime = SDL_GetTicks();
        }
        else
            SDL_Delay(DELAY - (lCurrentTime - lFormerTime));
    }

    DrawEndStatistics();

    long lPlayer;

    for ( int i = 0; i < m_sNbPlayers; i++)
    {
        lPlayer = m_vPlayers[i].GetPlayerBehaviour()->GetPoints();

        if ( lPlayer > m_lHighScore )
        {
            m_lHighScore = lPlayer;
            SaveHighScore();
        }
    }

    //we delete the players for further instances
    if ( m_vPlayers != NULL )
    {
        delete[] m_vPlayers;
        m_vPlayers = NULL;
    }
}
